package com.bestattempt.utility.reflection;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Arrays;

/**
 * TODO: Document me
 *
 * @author Ganeshji Marwaha
 * @since Oct 14, 2006
 */
public class ReflectionUtil {
    private static Log log = LogFactory.getLog(ReflectionUtil.class);

    public static Field getField(Object obj, String fieldStr, boolean forceAccesible) {
        try {
            Field field = null;
            Field[] fields = getAllInstanceFields(obj.getClass());
            for(Field tempField: fields) {
                if(tempField.getName().equals(fieldStr)) {
                    field = tempField;
                    break;
                }
            }
            if(field == null) {
                throw new NoSuchFieldException("Field with name " + fieldStr + " is not found in class " + obj.getClass().getSimpleName());
            }

            if( (forceAccesible) && (!Modifier.isPublic(field.getModifiers())) ) {
                field.setAccessible(true);
            }
            return field;
        } catch (Exception e) {
            throw new ReflectionException("ReflectionException", e);
        }
    }

    public static Field[] getAllInstanceFields(Class clazz) {
        List<Field> fields = new ArrayList<Field>();
        while(clazz != null) {
            Field[] fieldArr = clazz.getDeclaredFields();
            for(Field field: fieldArr) {
                if(!Modifier.isStatic(field.getModifiers())) {
                    fields.add(field);
                }
            }
            clazz = clazz.getSuperclass();
        }
        return fields.toArray(new Field[fields.size()]);
    }

    public static Object getFieldValue(Object obj, Field field) {
        try {
            return field.get(obj);
        } catch (Exception e) {
            throw new ReflectionException("ReflectionException", e);
        }
    }

    public static void setFieldValue(Object obj, Field field, Object value) {
        try {
            field.set(obj, value);
        } catch (Exception e) {
            throw new ReflectionException("ReflectionException", e);
        }
    }

    public static <D> D newInstance(Class<D> clazz) {
        try {
            return clazz.newInstance();
        } catch (Exception e) {
            throw new ReflectionException("ReflectionException", e);
        }
    }

    public static <D> D newInstance(Type type) {
        return newInstance((Class<D>) type);
    }



    private static void handleException(Exception e) {
        log.error("Reflection Exception", e);
    }
}
